﻿//代表一个Sencha应用程序. 大部分应用程序至少由应用程序的name和一个launch函数组成
Ext.Application = Ext.extend(Ext.util.Observable, {


    //launch函数的作用域。 默认为当前Application实例
    scope: undefined,

    //为True时自动设立Ext.History支持(默认为true)
    useHistory: true,

    //如果为true，每当检测到一个应用程序/设备配置变化时，会自动调用的所有组件Ext.Component.setProfile方法
    autoUpdateComponentProfiles: true,

    //如果为true，则采用当前应用程序配置属性，并调用updateComponentProfiles方法。默认为true
    setProfilesOnLaunch: true,



    constructor: function (config) {
        this.addEvents(

            'launch', //当应用程序launch函数执行后触发

            //当在应用程序检测到配置属性发生变化时触发
            //这发生在应用程序的各组件由于配置属性发生变化而更新之前
            //任何侦函数返回false都可以取消应用程序各组件的自动更新
            'beforeprofilechange',

            //当应用程序配置属性发生变化，且应用程序各组件更新后触发，以执行额外的处理
            'profilechange'
        );

        Ext.Application.superclass.constructor.call(this, config);

        this.bindReady();//调用Ext.onReady方法

        //实例化一个应用程序时创建一个全局变量，同时为应用程序内部的views，stores，models和controllers创建命名空间
        var name = this.name;

        if (name) {
            window[name] = this;

            Ext.ns(
                name,
                name + ".views",
                name + ".stores",
                name + ".models",
                name + ".controllers"
            );
        }

        if (Ext.addMetaTags) {
            Ext.addMetaTags(config);//添加html页面头部标签元素
        }
    },


    bindReady: function () {
        //Ext.onReady = Ext.EventManager.onDocumentReady;
        Ext.onReady(this.onReady, this); //在Ext.onReady函数执行过程中调用以参数形式传入的this.onReady方法
    },

    //当页面完全加载时自动调用。
    //这是一个空的函数，如果应用程序需要在页面加载后执行动作，则重写该方法
    launch: Ext.emptyFn,

    //设为true时，DOM创建完成后自动删除应用程序的加载掩码元素
    //如果设置为true，将产生一个名为“loading-mask”的DIV来挡住body
    //如果使用自定义的加载掩码元素，则传入一些其他的DOM节点的ID。默认为false。
    useLoadMask: false,

    //掩码元素多少毫秒后淡化
    loadMaskFadeDuration: 1000,

    //掩码元素从开始淡化到消失用多少毫秒
    loadMaskRemoveDuration: 1050,

    //如果为true，则通过调用Ext.Viewport的init方法设置在全屏下的工作模式（默认为true）
    autoInitViewport: true,

    //Dispatches to a given controller/action combo with optional arguments.
    dispatch: function (options) {
        return Ext.dispatch(options); //Dispatches a request to a controller action
    },

    //初始化掩码元素
    initLoadMask: function () {
        var useLoadMask = this.useLoadMask,
            defaultId = 'loading-mask',
            //使用自定义的useLoadMask还是使用默认defaultId。自定义useLoadMask为代表页面元素id的字符串，
            //使用默认defaultId则useLoadMask设为布尔值true
            loadMaskId = typeof useLoadMask == 'string' ? useLoadMask : defaultId; 

        if (useLoadMask) {//如果使用掩码元素
            if (loadMaskId == defaultId) {
                Ext.getBody().createChild({ id: defaultId });//创建id为默认id的掩码元素
            }

            var loadingMask = Ext.get('loading-mask'),//获取默认id的掩码元素
                fadeDuration = this.loadMaskFadeDuration, //掩码元素多少毫秒后淡化
                hideDuration = this.loadMaskRemoveDuration; //掩码元素从开始淡化到消失用多少毫秒

            Ext.defer(function () {
                loadingMask.addCls('fadeout');

                Ext.defer(function () {
                    loadingMask.remove();
                }, hideDuration); //延时hideDuration时间后消失

            }, fadeDuration);//延时fadeDruation时间后淡化
        }
    },


    onBeforeLaunch: function () {
        var History = Ext.History, //Ext.History = new Ext.util.Observable
            useHistory = History && this.useHistory, //为True时自动设立Ext.History支持(默认为true)

            //获取当前配置名，determineProfile为true表示不触发profilechange事件
            profile = this.determineProfile(true);

        //如果设立了Ext.History支持
        if (useHistory) {
            this.historyForm = Ext.getBody().createChild({//在body元素下面创建子元素
                id: 'history-form',
                cls: 'x-hide-display',
                style: 'display: none;',
                tag: 'form',
                action: '#',
                children: [
                    {
                        tag: 'div',
                        children: [
                            {
                                tag: 'input',
                                id: History.fieldId,
                                type: 'hidden'
                            },
                            {
                                tag: 'iframe',
                                id: History.iframeId
                            }
                        ]
                    }
                ]
            });

            History.init();//循环侦听浏览器url锚点的变化，保证History的锚点是浏览器当前锚点
            History.on('change', this.onHistoryChange, this);//锚点变化时触发

            var token = History.getToken();//获取当前锚点

            if (this.launch.call(this.scope || this, profile) !== false) {//运行launch函数，如果返回结果不为false
                //发送给controller动作的一个请求, 添加到History堆栈，并在必要时更新页面的URL
                Ext.redirect(token || this.defaultUrl || { controller: 'application', action: 'index' });
            }
        } else {//如果没有设立Ext.History支持
            this.launch.call(this.scope || this, profile);//运行launch函数
        }

        this.launched = true;//已经运行过launch函数

        this.fireEvent('launch', this);//触发"launch"事件

        //如果为true，则采用当前应用程序配置属性，并调用updateComponentProfiles方法。默认为true
        if (this.setProfilesOnLaunch) {
            //遍历所有已创建的组件，调用每个组件的setProfile方法，以profile为参数
            this.updateComponentProfiles(profile);
        }
    },


    onReady: function () {
        if (this.useLoadMask) {//如果使用掩码元素
            this.initLoadMask(); //初始化掩码元素
        }

        //添加当移动设备摆放方向改变时的侦听函数
        Ext.EventManager.onOrientationChange(this.determineProfile, this);

        //this.autoInitViewport如果为true，则通过调用Ext.Viewport的init方法设置在全屏下的工作模式（默认为true）
        if (this.autoInitViewport) {
            //设全局宽高,然后延时一段时间后运行this.onBeforeLaunch方法
            Ext.Viewport.init(this.onBeforeLaunch, this);
        } else {
            this.onBeforeLaunch();
        }

        return this;
    },

    //调用profiles中的每个配置函数，标志第一个返回true的函数的配置名，作为当前应用程序配置属性。
    //如果配置属性发生了变化，则触发“beforeprofilechange'和'profilechange”事件
    determineProfile: function (silent) {
        var currentProfile = this.currentProfile, //当前配置名，属于this.profiles的属性
            profiles = this.profiles, //一组以当前application支持的规范命名的配置名
            name;

            //例：
            //profiles: {
            //    phone: function() {
            //        return Ext.is.Phone;
            //    },
            //    tabletPortrait: function() {
            //        return Ext.is.Tablet && Ext.orientation == 'portrait';
            //    },
            //    tabletLandscape: function() {
            //        return Ext.is.Tablet && Ext.orientation == 'landscape';
            //    }
            //}
        for (name in profiles) {
            if (profiles[name]() === true) {

                //'beforeprofilechange': 当在应用程序检测到配置文件发生变化时触发
                if (name != currentProfile && this.fireEvent('beforeprofilechange', name, currentProfile) !== false) {

                    //如果this.autoUpdateComponentProfiles为true，每当检测到一个应用程序/设备配置变化时，
                    //会自动调用的所有组件Ext.Component.setProfile方法(默认为true)
                    if (this.autoUpdateComponentProfiles) {
                        //遍历所有已创建的组件，调用每个组件的setProfile方法，以name为参数
                        this.updateComponentProfiles(name); 
                    }

                    if (silent !== true) {//如果silent为true, 则不触发profilechange事件
                        this.fireEvent('profilechange', name, currentProfile);
                    }
                }

                this.currentProfile = name;//设置当前配置名
                break;
            }
        }

        return this.currentProfile;//返回当前配置名
    },

    //遍历所有已创建的组件，调用每个组件的setProfile方法，以profile为参数
    updateComponentProfiles: function (profile) {
        Ext.ComponentMgr.each(function (key, component) {
            if (component.setProfile) {
                component.setProfile(profile);
            }
        });
    },

    //获取当前配置属性
    getProfile: function () {
        return this.currentProfile;
    },

    //锚点变化时触发
    onHistoryChange: function (token) {
        //发送给controller动作的一个请求, 添加到History堆栈，并在必要时更新页面的URL
        return Ext.redirect(token);
    }
});